#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <unordered_map>
#include <cassert>
#include "Sock.hpp"
#include "Epoller.hpp"
#include "Log.hpp"
#include "Err.hpp"
#include "Util.hpp"

namespace tcpserver
{
    class Connection;
    class TcpServer;

    static const uint16_t defaultport = 8080;

    using func_t = std::function<void(Connection *)>;

    // 管理每一个连接，每一个连接都要有自己的暂存读写缓冲区，把它当成一个大号结构体来用
    // 因为不能保证本轮数据读完就能读到一个完整的请求，所以读到的内容要先放到自己的读缓冲区中
    // 写缓冲区也是如此，你怎么知道你写到哪个地方呢？
    // 未来服务器会有大量的连接到来，那需不需要把这些连接管理起来呢？要的！
    // 怎么管？先描述，在组织
    // 怎么描述？下面的Connection已经描述好了
    // 怎么组织？用unordered_map管理起来
    class Connection
    {
    public:
        Connection(int sockfd)
            : _sockfd(sockfd), _tsp(nullptr)
        {
        }

        void Register(func_t r, func_t s, func_t e)
        {
            _recver = r;
            _sender = s;
            _excepter = e;
        }

        ~Connection()
        {
        }

    public:
        int _sockfd;

        std::string inbuffer;  // 输入缓冲区
        std::string outbuffer; // 输出缓冲区

        // 设置读，写， 处理异常方法
        func_t _recver;   // 从_sockfd读
        func_t _sender;   // 向_sockfd写
        func_t _excepter; // 处理IO的异常事件

        TcpServer *_tsp;
    };

    class TcpServer
    {
    public:
        TcpServer(const uint16_t port = defaultport)
            : _port(port)
        {
        }

        void InitServer()
        {
            // 创建socket
            _sock.Socket();
            _sock.Bind(_port);
            _sock.Listen();

            // 构建epoll模型
            _epoller.Create();

            // 截至目前唯一的listensock也是一个connection，所以要管理起来
            // 由于这个连接是listensock的，所以我们只需要设置"读"回调方法，也就是Accept
            AddConnection(_sock.Getfd(), EPOLLIN | EPOLLET, std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
        }

        // 添加连接，这时候需要告诉我你这个connection想要在未来时间就绪时执行什么方法
        // 前两个参数相当于告诉epoll帮我关心哪个fd的哪些events
        // 后三个参数相当于epoll通知我们事件就绪时这个connection应该执行哪些方法
        void AddConnection(int sockfd, const uint32_t events, func_t recver, func_t sender, func_t excepter)
        {
            // 1. 为每一个sockfd创建Connection对象，并初始化
            Connection *conn = new Connection(sockfd);
            // 给fd设置对应的回调方法
            conn->Register(recver, sender, excepter);

            // 2. 将这个sockfd和它要关心的事件"写透式"地添加到epoll模型当中
            // 2.1 如果epoll的工作模式是ET的话，要先设置对应的fd为非阻塞
            if (events & EPOLLET) // 这里判断再严谨一点，设置了ET模式的fd才将它设成非阻塞
                Util::setNonBlock(_sock.Getfd());
            // 2.2 添加到epoll模型当中
            bool ret = _epoller.AddEvent(sockfd, events);
            assert(ret);
            (void)ret;

            // 3. 将connection对象添加到服务器管理的_connections当中管理起来
            _connections.insert(std::pair<int, Connection *>(sockfd, conn));
        }

        void Accepter(Connection *conn)
        {

        }

        ~TcpServer()
        {
        }

    private:
        uint16_t _port;
        Sock _sock;
        Epoller _epoller;
        std::unordered_map<int, Connection *> _connections;
    };

} // namespace tcpserver